package com.turbo.cloud.minio.web.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.services.s3.model.S3Object;
import com.turbo.cloud.core.exception.BusinessException;
import com.turbo.cloud.core.security.utils.SecurityUtil;
import com.turbo.cloud.core.utils.UUIDUtil;
import com.turbo.cloud.minio.config.MinioConfig;
import com.turbo.cloud.minio.template.OssTemplate;
import com.turbo.cloud.minio.web.entity.MinioFile;
import com.turbo.cloud.minio.web.service.IMinioFileService;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * Minio文件服务
 *
 * @author zhangluning
 */
@RequiredArgsConstructor
@Component
public class MinioFileServiceImpl implements IMinioFileService {

    final MinioConfig minioConfig;
    final MinioClient minioClient;
    final OssTemplate ossTemplate;

    /**
     * 查看存储bucket是否存在
     *
     * @param bucketName 桶名
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        if (StrUtil.isBlank(bucketName)) {
            throw new BusinessException("Minio桶名不能为空");
        }
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 桶名
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 桶名
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            throw new BusinessException("获取Minio全部Bucket异常");
        }
    }

    /**
     * 文件上传
     *
     * @param file       文件
     * @param bucketName 桶名
     * @return Boolean
     */
    public MinioFile upload(MultipartFile file, String bucketName, String businessType, String businessNo, String uploadBy) {

        if (Objects.isNull(file)) {
            throw new BusinessException("上传Minio文件不能为空");
        }

        if (!bucketExists(bucketName)) {
            throw new BusinessException("Minio桶名不存在");
        }

        if (StrUtil.isBlank(businessType)) {
            throw new BusinessException("上传Minio文件业务类型不能为空");
        }

        if (StrUtil.isBlank(businessNo)) {
            throw new BusinessException("上传Minio文件业务编码不能为空");
        }

        if (StrUtil.isBlank(uploadBy)) {
            throw new BusinessException("上传Minio文件上传人不能为空");
        }

        MinioFile minioFile = new MinioFile();

        String originalFilename = file.getOriginalFilename();
        String fileName = UUIDUtil.generateSimpleUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMdd");
        String objectName = yyyyMMdd + "/" + fileName;

        minioFile.setFileId(objectName);
        minioFile.setFileName(fileName);
        minioFile.setOldFileName(originalFilename);

        try {
            String uploadDate = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
            minioFile.setUploadBy(uploadBy);
            minioFile.setUploadDate(uploadDate);
            minioFile.setBusinessType(businessType);
            minioFile.setBusinessNo(businessNo);

            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            throw new BusinessException("上传Minio文件异常");
        }

        String url = getFileUrl(objectName, bucketName);
        minioFile.setFileUrl(url);

        return minioFile;
    }

    /**
     * 文件上传
     *
     * @param file       文件
     * @param bucketName 桶名
     * @return Boolean
     */
    public MinioFile uploadByAuth(MultipartFile file, String bucketName) {

        if (Objects.isNull(file)) {
            throw new BusinessException("上传Minio文件不能为空");
        }

        if (!bucketExists(bucketName)) {
            throw new BusinessException("Minio桶名不存在");
        }

        MinioFile minioFile = new MinioFile();

        String originalFilename = file.getOriginalFilename();
        String fileName = UUIDUtil.generateSimpleUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMdd");
        String objectName = yyyyMMdd + "/" + fileName;

        minioFile.setFileId(objectName);
        minioFile.setFileName(fileName);
        minioFile.setOldFileName(originalFilename);

        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            throw new BusinessException("上传Minio文件异常");
        }

        String url = getFileUrl(objectName, bucketName);
        minioFile.setFileUrl(url);

        return minioFile;
    }

    /**
     * 预览
     *
     * @param fileId     文件编码
     * @param bucketName 桶名
     * @return
     */
    public String getFileUrl(String fileId, String bucketName) {
        if (!bucketExists(bucketName)) {
            throw new BusinessException("Minio桶名不存在");
        }
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileId).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            throw new BusinessException("获取Minio文件地址异常");
        }
    }

    /**
     * 预览
     *
     * @param fileId     文件编码
     * @param bucketName 桶名
     * @return
     */
    public String getExpiryFileUrl(String fileId, String bucketName, int expiry) {
        if (!bucketExists(bucketName)) {
            throw new BusinessException("Minio桶名不存在");
        }
        // 失效时间（以秒为单位），默认是7天，不得大于七天
        if (Objects.isNull(expiry)) {
            //失效时间（以秒为单位），默认是7天
            expiry = 24 * 60 * 60;
        } else if(expiry > (7 * 24 * 60 * 60)){
            throw new BusinessException("Minio文件链接失效时间不得大于7天");
        }
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileId).method(Method.GET).expiry(expiry).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            throw new BusinessException("获取Minio文件地址异常");
        }
    }

    /**
     * 文件下载
     *
     * @param response        response
     * @param fileId     文件编码
     * @param bucketName 桶名
     * @return Boolean
     */
    public void download(HttpServletResponse response, String fileId, String bucketName) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(fileId).build();
        try (GetObjectResponse getObjectResponse = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = getObjectResponse.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                response.setCharacterEncoding("utf-8");
                // 设置强制下载不打开
                response.setContentType("application/force-download");
                response.addHeader("Content-Disposition", "attachment;fileName=" + fileId);
                try (ServletOutputStream stream = response.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            throw new BusinessException("下载Minio文件异常");
        }
    }

    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileId     文件编码
     * @param bucketName 桶名
     * @return boolean
     */
    public boolean remove(String fileId, String bucketName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileId).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 通过bucketName和objectName获取对象
     *
     * @param fileId
     * @param bucketName 桶名
     * @return
     */
    public S3Object getObject(String fileId, String bucketName){
        try {
            return ossTemplate.getObject(bucketName, fileId);
        } catch (Exception e) {
            return null;
        }
    }
}
